Extract service option prviders for non-standard Omniauth payloads (#1655)

* Extract option prviders for non-standard Omniauth payloads

- should enable huginn_agent gem developers to add their own providers
- devs would reopen OmniauthCallbacksController and define their specific action, supplying their own option provider

* Creates a registry for option providers on the Service class

- enables huginn_agent gem developers to register their own omniauth option interpreters

* Cleans up changes to the global Service class after the spec

Will Read 7 年之前
父节点
当前提交
6b459ef412
共有 3 个文件被更改,包括 55 次插入18 次删除
  1. 21 10
      app/models/service.rb
  2. 9 8
      spec/controllers/omniauth_callbacks_controller_spec.rb
  3. 25 0
      spec/models/service_spec.rb

+ 21 - 10
app/models/service.rb

@@ -57,17 +57,8 @@ class Service < ActiveRecord::Base
57 57
     (config = Devise.omniauth_configs[provider.to_sym]) && config.args[1]
58 58
   end
59 59
 
60
-  def self.provider_specific_options(omniauth)
61
-    case omniauth['provider'].to_sym
62
-      when :'37signals'
63
-        { user_id: omniauth['extra']['accounts'][0]['id'], name: omniauth['info']['name'] }
64
-      else
65
-        { name: omniauth['info']['nickname'] || omniauth['info']['name'] }
66
-    end
67
-  end
68
-
69 60
   def self.initialize_or_update_via_omniauth(omniauth)
70
-    options = provider_specific_options(omniauth)
61
+    options = get_options(omniauth)
71 62
 
72 63
     find_or_initialize_by(provider: omniauth['provider'], uid: omniauth['uid'].to_s).tap do |service|
73 64
       service.assign_attributes token: omniauth['credentials']['token'],
@@ -78,4 +69,24 @@ class Service < ActiveRecord::Base
78 69
                                 options: options
79 70
     end
80 71
   end
72
+
73
+  def self.register_options_provider(provider_name, &block)
74
+    option_providers[provider_name] = block
75
+  end
76
+
77
+  def self.get_options(omniauth)
78
+    option_providers.fetch(omniauth['provider'], option_providers['default']).call(omniauth)
79
+  end
80
+
81
+  private
82
+  @@option_providers = HashWithIndifferentAccess.new
83
+  cattr_reader :option_providers
84
+
85
+  register_options_provider('default') do |omniauth|
86
+    {name: omniauth['info']['nickname'] || omniauth['info']['name']}
87
+  end
88
+
89
+  register_options_provider('37signals') do |omniauth|
90
+    {user_id: omniauth['extra']['accounts'][0]['id'], name: omniauth['info']['name']}
91
+  end
81 92
 end

+ 9 - 8
spec/controllers/omniauth_callbacks_controller_spec.rb

@@ -5,22 +5,23 @@ describe OmniauthCallbacksController do
5 5
     sign_in users(:bob)
6 6
     OmniAuth.config.test_mode = true
7 7
     request.env["devise.mapping"] = Devise.mappings[:user]
8
-    request.env["omniauth.auth"] = JSON.parse(File.read(Rails.root.join('spec/data_fixtures/services/twitter.json')))
9 8
   end
10 9
 
11 10
   describe "accepting a callback url" do
12 11
     it "should update the user's credentials" do
12
+      request.env["omniauth.auth"] = JSON.parse(File.read(Rails.root.join('spec/data_fixtures/services/twitter.json')))
13 13
       expect {
14 14
         get :twitter
15 15
       }.to change { users(:bob).services.count }.by(1)
16 16
     end
17
+  end
17 18
 
18
-    # it "should work with an unknown provider (for now)" do
19
-    #   request.env["omniauth.auth"]['provider'] = 'unknown'
20
-    #   expect {
21
-    #     get :unknown
22
-    #   }.to change { users(:bob).services.count }.by(1)
23
-    #   expect(users(:bob).services.first.provider).to eq('unknown')
24
-    # end
19
+  describe "handling a provider with non-standard omniauth options" do
20
+    it "should update the user's credentials" do
21
+      request.env["omniauth.auth"] = JSON.parse(File.read(Rails.root.join('spec/data_fixtures/services/37signals.json')))
22
+      expect {
23
+        get "37signals"
24
+      }.to change { users(:bob).services.count }.by(1)
25
+    end
25 26
   end
26 27
 end

+ 25 - 0
spec/models/service_spec.rb

@@ -98,6 +98,7 @@ describe Service do
98 98
       expect(service.token).to eq('a1b2c3d4...')
99 99
       expect(service.secret).to eq('abcdef1234')
100 100
     end
101
+
101 102
     it "should work with 37signals services" do
102 103
       signals = JSON.parse(File.read(Rails.root.join('spec/data_fixtures/services/37signals.json')))
103 104
       expect {
@@ -113,6 +114,7 @@ describe Service do
113 114
       expect(service.options[:user_id]).to eq(12345)
114 115
       service.expires_at = Time.at(1401554352)
115 116
     end
117
+
116 118
     it "should work with github services" do
117 119
       signals = JSON.parse(File.read(Rails.root.join('spec/data_fixtures/services/github.json')))
118 120
       expect {
@@ -126,4 +128,27 @@ describe Service do
126 128
       expect(service.token).to eq('agithubtoken')
127 129
     end
128 130
   end
131
+
132
+  describe 'omniauth options provider registry for non-conforming omniauth responses' do
133
+    describe '.register_options_provider' do
134
+      before do
135
+        Service.register_options_provider('test-omniauth-provider') do |omniauth|
136
+          { name: omniauth['special_field'] }
137
+        end
138
+      end
139
+
140
+      after do
141
+        Service.option_providers.delete('test-omniauth-provider')
142
+      end
143
+
144
+      it 'allows gem developers to add their own options provider to the registry' do
145
+        actual_options = Service.get_options({
146
+          'provider' => 'test-omniauth-provider',
147
+          'special_field' => 'A Great Name'
148
+        })
149
+
150
+        expect(actual_options[:name]).to eq('A Great Name')
151
+      end
152
+    end
153
+  end
129 154
 end